/**
 * 对象操作工具类
 */
export class ObjUtil {
    /**
     * 对日期进行格式化， 和C#大致一致 默认yyyy-MM-dd HH:mm:ss
     * 可不带参数 一个日期参数 或一个格式化参数
     * @param date 要格式化的日期
     * @param format 进行格式化的模式字符串
     *     支持的模式字母有：
     *     y:年,
     *     M:年中的月份(1-12),
     *     d:月份中的天(1-31),
     *     H:小时(0-23),
     *     h:小时(0-11),
     *     m:分(0-59),
     *     s:秒(0-59),
     *     f:毫秒(0-999),
     *     q:季度(1-4)
     * @return String
     * @author adswads@gmail.com
     */
    public dateFormat(date?: any, format?: string): string {
        //无参数
        if (date == undefined && format == undefined) {
            date = new Date();
            format = "yyyy-MM-dd HH:mm:ss";
        }
        //无日期
        else if (typeof date == "string") {
            format = date;
            date = new Date();
        }
        //无格式化参数
        else if (format === undefined) {
            format = "yyyy-MM-dd HH:mm:ss";
        } else {
        }
        //没有分隔符的特殊处理
        var map = {
            y: date.getFullYear() + "", //年份
            M: date.getMonth() + 1 + "", //月份
            d: date.getDate() + "", //日
            H: date.getHours(), //小时 24
            m: date.getMinutes() + "", //分
            s: date.getSeconds() + "", //秒
            q: Math.floor((date.getMonth() + 3) / 3) + "", //季度
            f: date.getMilliseconds() + "", //毫秒
        };
        //小时 12
        if (map["H"] > 12) {
            map["h"] = map["H"] - 12 + "";
        } else {
            map["h"] = map["H"] + "";
        }
        map["H"] += "";
        var reg = "yMdHhmsqf";
        var all = "",
            str = "";
        for (var i = 0, n = 0; i < reg.length; i++) {
            n = format.indexOf(reg[i]);
            if (n < 0) {
                continue;
            }
            all = "";
            for (; n < format.length; n++) {
                if (format[n] != reg[i]) {
                    break;
                }
                all += reg[i];
            }
            if (all.length > 0) {
                if (all.length == map[reg[i]].length) {
                    str = map[reg[i]];
                } else if (all.length > map[reg[i]].length) {
                    if (reg[i] == "f") {
                        str =
                            map[reg[i]] +
                            this.charString("0", all.length - map[reg[i]].length);
                    } else {
                        str =
                            this.charString("0", all.length - map[reg[i]].length) +
                            map[reg[i]];
                    }
                } else {
                    switch (reg[i]) {
                        case "y":
                            str = map[reg[i]].substr(map[reg[i]].length - all.length);
                            break;
                        case "f":
                            str = map[reg[i]].substr(0, all.length);
                            break;
                        default:
                            str = map[reg[i]];
                            break;
                    }
                }
                format = format.replace(all, str);
            }
        }
        return format;
    }

    /**
     * 返回字符串 为n个char构成
     * @param char 重复的字符
     * @param count 次数
     * @return String
     * @author adswads@gmail.com
     */
    public charString(char: string, count: number): string {
        var str: string = "";
        while (count--) {
            str += char;
        }
        return str;
    }

    /**
     * 在一个数组里删除特定某个元素的方法。================
     * @param array 要被删除数据的数组。
     * @param item 数组里要被剔除的元素。
     */
    removeItemInArray(array: any[], item) {
        let index = array.indexOf(item);
        if (index > -1) {
            array.splice(index, 1);
        }
    }

    /**
     * 更替数组
     * @param target 需要更换目标数组
     * @param newArr 用来插入目标数组的数组
     */
    replaceArr(target: any[], newArr: any[]) {
        target.splice(0);
        target.push(...newArr);
    }

    /**
     * 通过指定的属性名在数组查找对应的元素
     * @param arr 需要查找数组
     * @param value 需要找到的属性对应的值
     * @param attr 查找的属性名
     */
    findArrByValue(arr: any[], value: any, attr: string) {
        return arr[arr.findIndex((item) => item[attr] == value)];
    }

    /**
     * 数组转化索引数组(对象)
     * @param arr 需要转化的数组
     * @param attr 需要拿来做索引的属性名
     */
    arrTransObj<T>(arr: T[], attr: string): { [propName: string]: T } {
        let obj: { [propName: string]: T } = {};
        arr.forEach((data) => {
            obj[data[attr]] = data;
        });
        return obj;
    }

    /**
     * 索引数组(对象)转换数组
     * @param obj 需要转换的索引数组(对象)
     * @returns 返回数组
     */
    objTransArr<T>(obj: { [propName: string]: T }) {
        let arr: T[] = [];
        for (let key in obj) {
            arr.push(obj[key]);
        }
        return arr;
    }

    /**
     * 数组强类型
     * @param obj
     * @param key
     */
    strongArr<T>(obj: { [propName: string]: T } | T[], key: string | number): T {
        return obj[key];
    }

    /** 克隆(不能是javascript对象) */
    clone<T>(obj: T): T {
        let o;
        switch (typeof obj) {
            case "undefined":
                break;
            case "string":
                o = obj + "";
                break;
            case "number":
                o = <any>obj - 0;
                break;
            case "boolean":
                o = obj;
                break;
            case "object":
                if (obj === null) {
                    o = null;
                } else {
                    if (obj instanceof Array) {
                        o = [];
                        for (let i = 0; i < obj.length; i++) {
                            o.push(this.clone(obj[i]));
                        }
                    } else {
                        o = {};
                        for (let k in obj) {
                            o[k] = this.clone(obj[k]);
                        }
                    }
                }
                break;
            default:
                o = obj;
                break;
        }
        return o;
    }

    assign<T>(target: T, source: T): T {
        return Object.assign(target, source);
    }

    /**
     * 生成GUID
     * @return {string}
     */
    guid(): string {
        function S4(): string {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        }
        let guid: string = "";
        for (let i = 0; i < 8; i++) {
            guid += S4();
        }
        return guid.toUpperCase();
    }

    /** 查找索引数组 */
    find<T>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => boolean
    ): T {
        for (let key in objs) {
            if (cb(objs[key], key)) return objs[key];
        }
        return undefined;
    }

    /**
     * 查找索引数组的key
     * @param objs
     * @param cb
     */
    findKey<T>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => boolean
    ): string {
        for (let key in objs) {
            if (cb(objs[key], key)) return key;
        }
        return undefined;
    }

    /** 索引数据map功能 */
    map<T, K>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => K
    ): K[] {
        let list: K[] = [];
        for (let key in objs) {
            list.push(cb(objs[key], key));
        }
        return list;
    }

    /** 索引数组循环遍历 */
    forEach<T>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => void
    ) {
        for (let key in objs) {
            cb(objs[key], key);
        }
    }

    /** 测试一个数组内的所有元素是否都能通过某个指定函数的测试 */
    every<T>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => boolean
    ): boolean {
        for (let key in objs) {
            if (!cb(objs[key], key)) return false;
        }
        return true;
    }

    /** 判断一个数组是否包含一个指定的值 */
    includes<T>(
        objs: { [propName: string]: T } | T[],
        cb: (obj: T, key?: string) => boolean
    ): boolean {
        for (let key in objs) {
            if (cb(objs[key], key)) return true;
        }
        return false;
    }
}

export const objUtil = new ObjUtil();
